package com.steak.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.steak.business.client.AccountClient;
import com.steak.business.client.IntegralClient;
import com.steak.business.client.OrderClient;
import com.steak.business.client.StockClient;
import com.steak.business.service.IBusinessService;
import com.steak.common.domain.dataobject.tcc.*;
import com.steak.common.domain.dto.*;
import com.steak.common.result.Response;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.AllArgsConstructor;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.Map;

/**
 * @author 刘牌
 * @date 2022/3/116:22
 */
@Component
@AllArgsConstructor
public class BusinessServiceImpl implements IBusinessService {
    private final IntegralClient integralClient;
    private final StockClient stockClient;
    private final OrderClient orderClient;
    private final AccountClient accountClient;
    /**
     * Try
     * @param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response placeOrder(Map<String,String> params){
        PlaceOrderDTO placeOrderDTO = JSON.parseObject(JSON.toJSONString(params), PlaceOrderDTO.class);
        FreezeStock freezeStock = stockClient.queryByUserId(placeOrderDTO.getUserId());
        if (null != freezeStock){
            throw new RuntimeException("abort the transaction");
        }
        //insert freeze stock record
        stockClient.saveFreezeStock(buildFreezeStock(placeOrderDTO));
        //update stock
        stockClient.decreaseStock(buildStockDTO(placeOrderDTO));
        //update integral
        integralClient.increaseIntegral(buildIntegralDTO(placeOrderDTO));
        //update account
        accountClient.decreaseBalance(buildAccountDTO(placeOrderDTO));
        return new Response(200,"place order success",null);
    }

    /**
     * Confirm
     * @param context
     * @return
     */
    @Override
    public void commitOrder(BusinessActionContext context) {
        Map<String,String> params = (Map<String, String>) context.getActionContext("params");
        Integer userId = Integer.valueOf(params.get("userId"));
        //update order status to 2
        String orderId =params.get("orderId");
        int confirmStatus = 2;
        orderClient.updateOrderStatus(buildUpdateOrderDTO(orderId,confirmStatus));
        //delete the freeze table
        stockClient.deleteFreezeRecordByUserId(userId);
        //update the freeze integral to 0
        integralClient.updateFreezeIntegralToZeroByUserId(userId);
        //update the account balance to 0
        accountClient.updateAccountBalanceToZeroById(userId);
    }

    /**
     * Cancel
     * @param context
     * @return
     */
    @Override
    public void rollbackOrder(BusinessActionContext context) {
        Map<String,String> params = (Map<String, String>) context.getActionContext("params");
        Integer userId = Integer.valueOf(params.get("userId").toString());
        //update the order status to 3 or delete the order
        String orderId = params.get("orderId").toString();
        Integer commodityId = Integer.valueOf(params.get("commodityId").toString());
        Integer count = Integer.valueOf(params.get("count").toString());
        Integer integral = Integer.valueOf(params.get("integral").toString());
        BigDecimal money = BigDecimal.valueOf(Long.parseLong(params.get("money").toString()));
        int confirmStatus = 3;
        orderClient.updateOrderStatus(buildUpdateOrderDTO(orderId,confirmStatus));
        //recovery stock and delete freeze record
        stockClient.updateStockNumByUserId(buildUpdateStockDTO(commodityId,count));
        stockClient.deleteFreezeRecordByUserId(userId);
        //recovery the integral balance
        integralClient.updateIntegralBalance(buildUpdateIntegralDTO(integral,userId));
        //recovery the account
        accountClient.updateAccountBalance(buildUpdateAccountDTO(userId,money));
    }

    public AccountDTO buildAccountDTO(PlaceOrderDTO placeOrderDTO){
        return new AccountDTO()
                .setUserId(placeOrderDTO.getUserId())
                .setMoney(placeOrderDTO.getMoney());
    }

    public IntegralDTO buildIntegralDTO(PlaceOrderDTO placeOrderDTO){
        return new IntegralDTO()
                .setIntegral(placeOrderDTO.getIntegral())
                .setUserId(placeOrderDTO.getUserId());
    }

    public StockDTO buildStockDTO(PlaceOrderDTO placeOrderDTO){
        return new StockDTO()
                .setCommodityId(placeOrderDTO.getCommodityId())
                .setCount(placeOrderDTO.getCount());
    }

    public FreezeStock buildFreezeStock(PlaceOrderDTO placeOrderDTO){
        return new FreezeStock()
                .setUserId(placeOrderDTO.getUserId())
                .setFreezeStock(placeOrderDTO.getCount())
                .setCommodityId(placeOrderDTO.getCommodityId());
    }

    public Order buildOrder(PlaceOrderDTO placeOrderDTO , Integer status){
        return new Order()
                .setUserId(placeOrderDTO.getUserId())
                .setCommodityId(placeOrderDTO.getCommodityId())
                .setCount(placeOrderDTO.getCount())
                .setMoney(placeOrderDTO.getMoney())
                .setOrderId(placeOrderDTO.getOrderId())
                .setStatus(status);
    }

    public UpdateOrderDTO buildUpdateOrderDTO(String orderId , Integer status){
        return new UpdateOrderDTO()
                .setStatus(status)
                .setOrderId(orderId);
    }

    public UpdateStockDTO buildUpdateStockDTO(Integer commodityId , Integer count){
        return new UpdateStockDTO()
                .setCommodityId(commodityId)
                .setCount(count);
    }

    public IntegralDTO buildUpdateIntegralDTO(Integer integral , Integer userId){
        return new IntegralDTO()
                .setIntegral(integral)
                .setUserId(userId);
    }

    public AccountDTO buildUpdateAccountDTO(Integer userId , BigDecimal money){
        return new AccountDTO()
                .setUserId(userId)
                .setMoney(money);
    }
}
